package server.command;

import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.StringTokenizer;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import communication.exception.NullResultExeption;
import communication.exception.ResultException;
import communication.exception.VerifyCommandException;
import communication.exception.VerifyParameterException;

import server.core.base.User;
import server.core.dao.DaoFactory.daoType;
import server.core.manager.Manager;
import server.core.util.FormatDate;
import server.command.list.*;

public class AnalysisCommand {
	protected static final Logger LOCAL_LOG=LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
//	static{
//		try {
//			File[] list = new File("/src/command/method").listFiles();
//			Class[] classInDir=new Class[list.length];
//			for(int i = 0; i < list.length; i++)
//				classInDir[i]=Class.forName("command.method."+list[i].getName().replace(".java", ""));
//			commandList=classInDir;
//		} catch (ClassNotFoundException e) {
//			e.printStackTrace();
//		}
//	}
	private static Class[] commandList=new Class[]{
		SearchFlight.class,
		ShowFlight.class,
		SearchTicket.class,
		ShowTicket.class,
		ReserveTicket.class,
		CancelReserve.class,
		PrintBase.class,
		SaveFlight.class,
		DeleteFlight.class
	};
	
	private User currentUser;
	private Class<? extends Manager> managerClass;
	private Class<?> command=null;
	private HashMap<String, String> paramMap=new HashMap<String,String>();
	
	private HashMap<String,Object> argsMap=new HashMap<String,Object>();
	//Parameters, using in Command`s Verification
	public static final String[] paramAirport={"FROMPORT","TOPORT"};
	public static final String[] paramAircraft={"AIRPLANE"};
	public static final String[] paramBetweenDate={"FROMDATE","TODATE"};
	public static final String[] paramDate={"DATE"};
	public static final String[] paramId={"ID"};
	public static final String[] paramBase={"BASE"};
	
	private Collection<?> result=null;

	private daoType typeOfDao;
	
	
	/**
	 * @param inputedCmd
	 * @param user
	 * @param type
	 * @throws VerifyParameterException
	 * @throws NullResultExeption
	 * @throws VerifyCommandException
	 * @throws ResultException
	 */
	public AnalysisCommand(String inputedCmd, User user, daoType type) throws VerifyParameterException, NullResultExeption, VerifyCommandException, ResultException{
		setCurrentUser(user);
		this.setTypeOfDao(type);
		StringTokenizer st=new StringTokenizer(inputedCmd.trim()," ");
		String commandName=st.nextToken();
		if( ! "HELP".equalsIgnoreCase(commandName)){
		for(Class<?> cm:commandList){
			if(cm.getSimpleName().equalsIgnoreCase(commandName)){
				try {
					managerClass = (Class<? extends Manager>) cm.getMethod("getManager", new Class[]{}).invoke(cm.newInstance());
					LOCAL_LOG.info("rule verif.");
				} catch (IllegalAccessException | IllegalArgumentException
						| SecurityException | InstantiationException | InvocationTargetException | NoSuchMethodException e) {
					e.printStackTrace();
				}
				if(currentUser.contentsManager(managerClass))
					command=cm;
				else
					throw new VerifyCommandException("User hasn't access");
			}
		}
		
		//grab param
		if(this.command==null)
			throw new VerifyParameterException("Incorrect command. Type \"help\" to print list of available commands");
		while(st.hasMoreTokens()){
			StringTokenizer param = new StringTokenizer(st.nextToken(),"=");
			String pKey=param.nextToken().toUpperCase();
			if(param.hasMoreTokens()){
				String pValue=param.nextToken().toUpperCase();
				paramMap.put(pKey,pValue);
			}
			else
				throw new VerifyParameterException("Incorrect parameter. Input as \"<parameter1>=<value> <parameter2>=<value>\"");
		}
		
		//call command
		try{
			Command newCmd=(Command) command.newInstance();
			newCmd.verifyParam(this);
			result=newCmd.execute();
			LOCAL_LOG.info("Calling command: "+commandName);
		}catch(VerifyParameterException | ResultException e){
			throw new NullResultExeption(e);
		}catch(NumberFormatException e){
			System.out.println("ID, date must be digital");
		}catch(NullPointerException e){
			System.out.println(e.getLocalizedMessage());
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		}
		else
			this.result=printHelp(currentUser);
	}

	public HashMap<String,String> getParamMap(){
		return this.paramMap;
	}
	public static Class[] getCommandList(){
		return commandList;
	}
	/**
	 * @param str
	 * @return date, converted String into Format Date
	 */
	public FormatDate changeStringIntoDate(String str) throws NumberFormatException, VerifyParameterException{
		if(8==str.length())
			return new FormatDate(
				Integer.parseInt(str.substring(0, 2)),
				Integer.parseInt(str.substring(2, 4)),
				Integer.parseInt(str.substring(4, 8)));
		else if(12==str.length())
			return new FormatDate(
					Integer.parseInt(str.substring(0, 2)),
					Integer.parseInt(str.substring(2, 4)),
					Integer.parseInt(str.substring(4, 8)),
					Integer.parseInt(str.substring(8, 10)),
					Integer.parseInt(str.substring(10, 12)));
		else{
			throw new VerifyParameterException(
					"Date must be 8-symbolic (Day'2'Mounth'2'Year'4') or 12-symbolic (Day'2'Mounth'2'Year'4'Hour'2'Minute'2')");
		}
	}
	
	public void setCurrentUser(User user){
		this.currentUser=user;
	}
	
	public User getCurrentUser(){
		return this.currentUser;
	}
	
	public Collection<?> getResult(){
		return this.result;
	}

	/**
	 * Key: string name of argument,
	 * Value: object of argument
	 * @return Map of arguments to call methods.
	 */
	public HashMap<String,Object> getArgsMap() {
		return argsMap;
	}

	public daoType getTypeOfDao() {
		return typeOfDao;
	}

	public void setTypeOfDao(daoType typeOfDao) {
		this.typeOfDao = typeOfDao;
	}
	
	/**
	 * @param user
	 * @return 
	 */
	public Collection<?> printHelp(User user){
		Collection<String> collection=new LinkedList<String>();
		LOCAL_LOG.info("PrintHelp: start");
		for(Class cm:AnalysisCommand.getCommandList()) {
			try {//get command's help invoking command
				managerClass = (Class<? extends Manager>) cm.getMethod("getManager", new Class[]{}).invoke(cm.newInstance());
			} catch (IllegalAccessException | IllegalArgumentException
					| SecurityException | InstantiationException | InvocationTargetException | NoSuchMethodException e) {
				e.printStackTrace();
			}
			if(currentUser.contentsManager(managerClass))
				try {
					collection.add(cm.getSimpleName()+((Command)cm.newInstance()).help());
				} catch (InstantiationException | IllegalAccessException e) {
					e.printStackTrace();
				}
		}
		return collection;
	}
}
